#include <dialects/RISCV/Dialect.h>
#include <dialects/RISCV/TypeAndProto.h>
#include <lcir/IR/Op.h>

#include <cstddef>

#include "dialects/PL/Ops.h"
namespace lcir::rv {
static bool checkIntBinaryType(const std::vector<Prototype *> ops) {
  if (ops.size() != 2) return false;
  if (!ops[0]->isa<IReg>() || !ops[1]->isa<IReg>()) return false;
  return true;
}
static std::vector<Prototype *> deduceIntBinaryType() { return {new IReg()}; }
static bool checkFloatBinaryType(const std::vector<Prototype *> ops) {
  if (ops.size() != 2) return false;
  if (!ops[0]->isa<FReg>() || !ops[1]->isa<FReg>()) return false;
  return true;
}
static std::vector<Prototype *> deduceFloatType() { return {new FReg()}; }

enum class Mode {
  d,
  w,
  h,
  b,
};
static constexpr const char *LoadModeStr[] = {"d", "w", "h", "b"};
class Load : public OpCRTP<Load, RVDialect> {
  long offset{0};

 public:
  Load(size_t offset = 0) : OpCRTP<Load, RVDialect>("load"), offset(offset) {}

  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    if (offset > 2048 || offset < -2047) return false;
    return operandVals.size() == 1 && operandVals[0]->isa<IReg>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return {new IReg()};
  }
};
class Store : public OpCRTP<Store, RVDialect> {
  long offset{0};

 public:
  Store(size_t offset = 0) : OpCRTP<Store, RVDialect>("store"), offset(offset) {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    if (offset > 2048 || offset < -2047) return false;
    return operandVals.size() == 2 && operandVals[0]->isa<IReg>() && operandVals[1]->isa<IReg>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return {};
  }
};
class LI : public OpCRTP<LI, RVDialect> {
 public:
  LI() : OpCRTP<LI, RVDialect>("li") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override {
    return operandVals.size() == 1 && operandVals[0]->isa<Imm>();
  }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return {new IReg()};
  }
  long getValue() const { return getOperands()[0]->getValue()->getProto<Imm>()->getNumber(); }
};
class Add : public OpCRTP<Add, RVDialect> {
  bool w{false};

 public:
  Add() : OpCRTP<Add, RVDialect>("add") {}
  Add(bool w) : OpCRTP<Add, RVDialect>("add"), w(w) {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return checkIntBinaryType(operandVals); }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return deduceIntBinaryType();
  }
  bool isW() const { return w; }
};
class Sub : public OpCRTP<Sub, RVDialect> {
  bool w{false};
 public:
  Sub() : OpCRTP<Sub, RVDialect>("sub") {}
  Sub(bool w) : OpCRTP<Sub, RVDialect>("sub"), w(w) {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return checkIntBinaryType(operandVals); }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return deduceIntBinaryType();
  }
  bool isW() const { return w; }
};
class Mul : public OpCRTP<Mul, RVDialect> {
 public:
  Mul() : OpCRTP<Mul, RVDialect>("mul") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return checkIntBinaryType(operandVals); }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return deduceIntBinaryType();
  }
};
class Div : public OpCRTP<Div, RVDialect> {
 public:
  Div() : OpCRTP<Div, RVDialect>("div") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return checkIntBinaryType(operandVals); }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return deduceIntBinaryType();
  }
};
class Rem : public OpCRTP<Rem, RVDialect> {
 public:
  Rem() : OpCRTP<Rem, RVDialect>("rem") {}
  bool checkType(const std::vector<lcir::Prototype *> &operandVals) override { return checkIntBinaryType(operandVals); }
  std::vector<lcir::Prototype *> deduceRetType(const std::vector<lcir::Prototype *> &operandProtos) override {
    return deduceIntBinaryType();
  }
};

}  // namespace lcir::rv